use serde::{Deserialize, Serialize};
use serde_json::Value;

use crate::error::Result;
use crate::prompt::{PromptDefinitionMetadata, PromptMessage};
use crate::resource::{ResourceContent, ResourceDefinitionMetadata};
use crate::server::{FastMcpServer, ServerMetadata};
use crate::tool::{ToolDefinitionMetadata, ToolResponse};

#[derive(Debug, Deserialize)]
#[serde(tag = "command", rename_all = "snake_case")]
pub enum ServerCommand {
    Metadata,
    ListTools,
    CallTool {
        name: String,
        #[serde(default)]
        arguments: Value,
    },
    ListResources,
    ReadResource {
        uri: String,
    },
    ListPrompts,
    InstantiatePrompt {
        name: String,
        #[serde(default)]
        arguments: Option<Value>,
    },
    Shutdown,
}

#[derive(Debug, Serialize, Clone)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum CommandResult {
    Metadata {
        data: ServerMetadata,
    },
    Tools {
        data: Vec<ToolDefinitionMetadata>,
    },
    ToolInvocation {
        data: ToolResponse,
    },
    Resources {
        data: Vec<ResourceDefinitionMetadata>,
    },
    Resource {
        data: ResourceContent,
    },
    Prompts {
        data: Vec<PromptDefinitionMetadata>,
    },
    PromptInstantiation {
        data: Vec<PromptMessage>,
    },
    Ack {
        message: String,
    },
}

impl CommandResult {
    pub fn shutdown_ack() -> Self {
        Self::Ack {
            message: "shutdown".into(),
        }
    }

    pub fn event_kind(&self) -> &'static str {
        match self {
            CommandResult::Metadata { .. } => "metadata",
            CommandResult::Tools { .. } => "tools",
            CommandResult::ToolInvocation { .. } => "tool_invocation",
            CommandResult::Resources { .. } => "resources",
            CommandResult::Resource { .. } => "resource",
            CommandResult::Prompts { .. } => "prompts",
            CommandResult::PromptInstantiation { .. } => "prompt_instantiation",
            CommandResult::Ack { .. } => "ack",
        }
    }
}

pub async fn execute_command(
    server: &FastMcpServer,
    command: ServerCommand,
) -> Result<(CommandResult, bool)> {
    let result = match command {
        ServerCommand::Metadata => CommandResult::Metadata {
            data: server.metadata(),
        },
        ServerCommand::ListTools => CommandResult::Tools {
            data: server.list_tools(),
        },
        ServerCommand::CallTool { name, arguments } => CommandResult::ToolInvocation {
            data: server.call_tool(&name, arguments).await?,
        },
        ServerCommand::ListResources => CommandResult::Resources {
            data: server.list_resources(),
        },
        ServerCommand::ReadResource { uri } => CommandResult::Resource {
            data: server.read_resource(&uri).await?,
        },
        ServerCommand::ListPrompts => CommandResult::Prompts {
            data: server.list_prompts(),
        },
        ServerCommand::InstantiatePrompt { name, arguments } => {
            CommandResult::PromptInstantiation {
                data: server.instantiate_prompt(&name, arguments.as_ref())?,
            }
        }
        ServerCommand::Shutdown => {
            return Ok((CommandResult::shutdown_ack(), true));
        }
    };

    Ok((result, false))
}
